package note_002;

import reflect.AutoRunClass;
import reflect.ReflectDemo7;
import reflect.TestMethod;
import reflect.Text5;

import java.io.File;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * JAVA反射机制
 * 反射是java的动态机制，它允许将对象的实例化，方法的调用，属性的操作等从编码期确定转移
 * 到程序运行期确定。这大大的提高了代码的灵活度。
 * 反射带来了更多的系统开销和较慢的运行效率，因此程序不能过度依赖反射。
 * 获取一个类的类对象有以下几种方式:
 * 1:类名.class
 * 例如:  Class cls = String.class;
 * Class cls = int.class;
 * 这种方式最直接，但是由于是靠硬编码形式写死，因此不够灵活。但是需要注意，
 * (基本类型只能通过这种方式获取类对象)
 * 2:Class.forName(String className)
 * Class cls = Class.forName("java.lang.String");
 * 3:类加载器ClassLoader
 */
public class day18 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, URISyntaxException {
        /** 1. 类名.class:获取一个类的类对象()*/
        // 这种方式最直接，但是由于是靠硬编码形式写死，因此不够灵活。但是需要注意，
        // (基本类型只能通过这种方式获取类对象)
/*
    Class cla = String.class;//获取操作类的类对象，一个Class的实例
    String name1 = cla.getName();//获取类的完全限定名:包名.类名
    name1 =cla.getSimpleName();//仅仅获取类名
    Method[] method = cla.getMethods();
    for(Method m :method) {//遍历获取所有方法名.
    System.out.println(m.getName());
    }
*/
        /** 2: Class.forName(String className):获取要实例化的类的类对象 */
/*
        Class cls = Class.forName("java.lang.String");//获取操作类的类对象，一个Class的实例
        String name = cls.getName();//获取类的完全限定名
        String name1 = cls.getSimpleName();//仅仅获取类名
        Method[] methods = cls.getMethods();//获取该类所有方法
        for (Method m : methods) {//遍历
            System.out.println(m.getName());
        }
*/
        /** 使用反射机制实例化对象
         *    (1).获取要实例化的类的类对象: Class.forName(String className)
         *    (2).可通过Class提供的方法
         *      .newInstance(): 类实例化,
         *      该方法要求此类必须具有无参构造器，它通过无参构造器实例化对象的.
         */
//        Class cls = Class.forName("reflect.Person");
//        Object obj = cls.newInstance();
//        System.out.println(obj);
        /** 使用特定的构造器实例化对象
         *      getConstructor():获取无参构造器
         *      getConstructor(String.class,int.class,...等):通过类对象获取特定的构造器
         */
//        Class cls = Class.forName("reflect.Person");//加载类对象
//        Constructor c = cls.getConstructor();//通过类对象获取特定的构造器(获取无参构造器)
//        Constructor c = cls.getConstructor(String.class,int.class);
//        Object o = c.newInstance("王五",30);
//        System.out.println(o);
        /**
         * 使用反射调用方法---------------------------------------------------反射调用方法
         * 无参方法
         * forName(String className):获取要实例化的类的类对象
         * newInstance():使用此方法可以有效地绕过编译器否则会执行的编译时异常检查
         * getMethod():可以获取此类的所有公开的方法，包含从超类继承的。
         * getMethods():获取该类所有方法
         * invoke():调用
         */
//        Class cls = Class.forName("reflect.Person");//实例化对
//        Object o = cls.newInstance();//反射
//        Method method = cls.getMethod("sayHello");获取方法
//        method.invoke(o);//[o.sayHello()] -> method(此获取了当前方法) ->通过-> .invoke开始调用(o的)方法
        /**
         * 调用有参方法
         */
//        Class cls = Class.forName("reflect.Person");
//        Object o = cls.newInstance();
//        Method m1 = cls.getMethod("say", String.class);//参数类型String.class
//        m1.invoke(o,"自己new对象");
//        Method m2 = cls.getMethod("say", String.class,int.class);
//        m2.invoke(o,"多点关心,多点爱",10);

        /**
         * 调用私有方法
         *
         * getMethod():可以获取此类的所有公开的方法，包含从超类继承的。
         * getDeclaredMethod():可以获取此类定义的所有方法，包含私有方法。
         *                    需要 XX.setAccessible(true) 打开访问权(强制,具有破坏性)
         */
//        Class cls = Class.forName("reflect.Person");//获取要实例化的类的类对象
//        Object o = cls.newInstance();//反射
//
//        Method m2 = cls.getDeclaredMethod("hehe");//获取私有方法
//        m2.setAccessible(true);//打开访问权
//        m2.invoke(o);
        /**
         * 加载资源时常用的相关路径---------------------------------------------加载资源时常用的相关路径
         * 常用的路径通常为类的加载路径，有两个:
         * 1:类对象 .getResource()
         *      与当前类所处同一个目录
         *      .toURI():转换成路径
         * 2:类加载器 .getResource()
         *      类加载路径。类的package定义中根包位置。
         *      getClassLoader():获取类加载器
         * 例如:有一个类:
         * 在WebServer类中，当我们使用上述两种方法获取路径时他们的对应位置为:
         * WebServer.class.getResource():当前WebServer所在的目录(编译后的Class文件所在目录)
         *
         * WebServer.class.getClassLoader().getResource()
         * 则是在WebServer的包的最上级，即com包的上一级.
         */
//        File file = new File(ReflectDemo7.class.getResource(".").toURI());
//        System.out.println(file);
        /*ReflectDemo7.class.getClassLoader().getResource("./XX/XX/某某获取下几级目录路径").toURI()*/
//        File file1 = new File(ReflectDemo7.class.getClassLoader().getResource(".").toURI() );
//        System.out.println(file1);


        /**
         * 注解---------------------------------------------------------注解
         * 注解可以辅助反射机制，例如帮助我们快速筛选在反射中操作的目标。他在如今流行的框架中被大量使用。
         *
         * java内置了一些注解，有些是辅助编译器时做验证使用。
         * 下面的两个注解就是java内置的注解，他们用于为我们定义的注解指定某些特性:
         * @Target:用于指定我们定义的注释可以被应用在那些，具体的位置被枚举类型:ElementYype.
         *      定义。例如:
         *      TYPE:在类上可以使用当期注解.
         *      METHOD:在方法上可以使用当前注解.
         *      FIELD:在属性上可以使用当前注解.
         *      还有一些其它的,可以参考API手册.
         * @Retention:用于指定我们定义的注解的保留级别.
         *      RetentionPolice.RUNTIME是最长用的，保留的在字节码文件中且在该类运行时可被反射机制利用。
         *      CLASS:保留在字节码中，但是反射机制不可用，默认就是该级别。
         *      SOURCE:保留在源码中.
         */
//        @Target(ElementType.TYPE)
//        @Retention(RetentionPolicy.RUNTIME)
//        public @interface AutoRunClass {
                /*
                注释里可以定义参数，格式:
                类型  参数名()
                类型:基本或String
                注意：如果当前注释只有一个参数时，通常参数名叫value

                当我们定义了参数，那么在使用当前注释时需要为参数指定值，格式:
                @注解名(参数名1=参数值1,参数名2=参数值2,...)
                参数指定的顺序可以与注解中定义的顺序不一样。

                列如:
                @AutoRunClass(5)
                注:上述注解没有指定参数名的原因是该注解只有一个参数，并且参数名叫value。
                即:int value();

                如果参数定义时为：int sum();
                那么使用注解时要写成:@TestMethod(sum=5)
             */
//                int value();//属性,类型:基本或String
//                int value() default 1;//default:设置默认值
//        }
        /**
         * 反射中使用注释
         *
         * boolean isAnnotationPresent(Class annolCls)
         *   检查是否有被annoCls指定的注释修饰
         *
         * getAnnotation():通过方法获取注解@TestMethod
         */
        /*加载Person的类对象*/
//        Class cls = Class.forName("reflect.Person");
//        if (cls.isAnnotationPresent(AutoRunClass.class)) {//判断指定注释
//            System.out.println(cls.getName() + ":被@AutoRunClass修饰");
//            Object obj = cls.newInstance();//反射
//            /*扫描当前类定义的所有方法*/
//            Method[] methods = cls.getDeclaredMethods();//获取所有方法
//            for (Method method : methods) {//遍历
//                if (method.isAnnotationPresent(TestMethod.class)) {//判断指定注释
//                    System.out.println(method.getName() + ":被@TestMethod修饰");
//                    TestMethod tm = method.getAnnotation(TestMethod.class);//获取注解
//                    int mou = tm.value();//获取参数值
//                    for (int i = 0; i < mou; i++) {
//                        method.invoke(obj);
//                    }
//                }
//            }
//        }
        /** 总结案例在Text5
         *
         * 扫描reflect包下的注解AutoRunClass的类进行实例化
         * 之后自动调用其中的被@TestMethod修饰的方法指定次(注释传入的参数)
         */
//        File file = new File(Text5.class.getResource(".").toURI());//加载相关路径
//        File[] files = file.listFiles(e->e.getName().endsWith(".class"));//获取.class后缀
//        for(File f : files) {//遍历名
//            String className1 = f.getName();
//            String className2 = className1.substring(0,className1.lastIndexOf("."));//拆分获取包名
//            Class cls = Class.forName("reflect."+className2);//获取实例化类对象
//            if (cls.isAnnotationPresent(AutoRunClass.class)) {//判断指定注释修饰
//                System.out.println("类："+cls.getName() + ":被@AutoRunClass修饰!");
//                Object obj = cls.newInstance();//反射
//                Method[] methods = cls.getDeclaredMethods();//获取所有当前类定义的方法
//                for (Method method : methods) {//遍历方法
//                    if(method.isAnnotationPresent(TestMethod.class)){//判断指定注释修饰
//                        System.out.println("方法:"+method.getName()+":被@TestMethod修饰!");
//                        TestMethod tm = method.getAnnotation(TestMethod.class);//获取指定注释
//                        int num = tm.value();//获取参数值
//                        for(int i=0;i<num;i++) {//通过参数执行相关业务操作
//                            method.invoke(obj);
//                        }
//                    }else {
//                        System.out.println("方法:"+method.getName()+":NO-被@TestMethod修饰!");
//                    }
//                }
//            }else {
//                System.out.println("类："+cls.getName() + ":NO-被@AutoRunClass修饰!");
//            }
//        }

        /**
         * 线程池
         * 线程池时线程的管理机制，主要有两个作用:
         * 1:重用线程。   延长线程的生命周期，使得线程的生命周期不随任务的结束而结束，从而避免出现，
         *              线程频繁创建与销毁带来的额外系统开销。
         * 2:控制线程数量。线程数量过多会过度的占用线程内存，严重时导致系统瘫痪。并且线程数量过多
         *              也会出现CPU过度切换导致并发性能地下.
         */
        //创建一个固定大小的线程池，这里的容量指定为2，即:线程池中有两条线程
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        /** .execute():执行
         * .shutdown():关闭线程(执行完当前线程任务)
         * .shutdownNow():强制结束当前线程(不在继续执行线程任务)*/


    }
}
